﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows;
using System.Windows.Input;
using System.Reflection;
using System.Windows.Controls;
using System.Windows.Threading;

namespace Kokomo.PeInspector.Reflection
{
	class FlowDocumentCodeWriter : CodeWriterBase
	{
		static FlowDocumentCodeWriter()
		{
			_textFormatItemTable = AssemblyAnalyzerEditor.CodeTextFormatItems.ToDictionary(i => i.TextType);
		}

		private static Dictionary<CodeTextType, CodeTextFormatItem> _textFormatItemTable;

		public FlowDocumentCodeWriter(
			FlowDocumentBuilder builder,
			ICommand navigateToCommand
			)
		{
			if (builder == null) throw new ArgumentNullException("builder");

			this.Builder = builder;
			this.NavigateToCommand = navigateToCommand;

			builder.Dispatcher.Invoke((Action)this.Initialize);
		}

		public FlowDocumentBuilder Builder { get; private set; }

		private static Style CreateStyle(
			CodeTextType textType
			)
		{
			Style style = null;// = new Style();

			CodeTextFormatItem formatItem;
			if (_textFormatItemTable.TryGetValue(textType, out formatItem))
			{
				style = formatItem.BuildStyle();

				//Brush foreground = formatItem.Foreground;
				//style.Setters.Add(new Setter(TextElement.ForegroundProperty, foreground));

				//if (formatItem.FontWeight != FontWeights.Normal)
				//    style.Setters.Add(new Setter(TextElement.FontWeightProperty, formatItem.FontWeight));
			}

			return style;
		}

		private void Initialize()
		{
			Dictionary<CodeTextType, Style> textStyles = new Dictionary<CodeTextType, Style>();

			Style style;

			// Normal
			style = CreateStyle(CodeTextType.Normal);
			this.NormalStyle = style;
			textStyles[CodeTextType.Normal] = style;

			textStyles[CodeTextType.Keyword] = CreateStyle(CodeTextType.Keyword);
			textStyles[CodeTextType.Operator] = CreateStyle(CodeTextType.Operator);
			textStyles[CodeTextType.Annotation] = CreateStyle(CodeTextType.Annotation);
			textStyles[CodeTextType.Comment] = CreateStyle(CodeTextType.Comment);
			textStyles[CodeTextType.TypeName] = CreateStyle(CodeTextType.TypeName);
			textStyles[CodeTextType.Declarator] = CreateStyle(CodeTextType.Declarator);
			textStyles[CodeTextType.Literal] = CreateStyle(CodeTextType.Literal);
			textStyles[CodeTextType.XmlElementName] = CreateStyle(CodeTextType.XmlElementName);
			textStyles[CodeTextType.XmlEntityName] = CreateStyle(CodeTextType.XmlEntityName);
			textStyles[CodeTextType.XmlAttributeName] = CreateStyle(CodeTextType.XmlAttributeName);
			textStyles[CodeTextType.XmlAttributeValue] = CreateStyle(CodeTextType.XmlAttributeValue);
			// Hyperlink
			style = new Style();
			style.Setters.Add(
				new Setter(
					Hyperlink.TextDecorationsProperty,
					new TextDecorationCollection() {
							new TextDecoration(
								TextDecorationLocation.Underline,
								new Pen(Brushes.Blue, 1) { DashStyle = DashStyles.Dot /* new DashStyle(new double[] {3, 4}, 0) */ },
								0,
								TextDecorationUnit.Pixel,
								TextDecorationUnit.Pixel
								)
						})
				);
			this.HyperlinkStyle = style;

			this._textStyles = textStyles;
		}

		#region Commands
		public ICommand NavigateToCommand { get; private set; }
		#endregion

		private Dictionary<CodeTextType, Style> _textStyles;

		public Style NormalStyle { get; set; }
		//public Style KeywordStyle { get; set; }
		//public Style TypeNameStyle { get; set; }
		//public Style DeclaratorStyle { get; set; }
		public Style HyperlinkStyle { get; set; }

		#region Writing stuff
		protected override void WriteTextOverride(string text, CodeTextType textType)
		{
			this.WriteStyledText(text, textType);
		}

		private Style DetermineStyle(CodeTextType textType)
		{
			Style textStyle;
			if (!this._textStyles.TryGetValue(textType, out textStyle))
				textStyle = this.NormalStyle;

			return textStyle;
		}
		private void WriteStyledText(string text, CodeTextType textType)
		{
			Style textStyle = this.DetermineStyle(textType);
			this.Builder.WriteStyledText(text, textStyle);
		}

		protected override void WriteMemberReferenceOverride(object target, string text, CodeTextType textType)
		{
			if (target == null) throw new ArgumentNullException("target");
			if (text == null) throw new ArgumentNullException("text");

			Style textStyle = this.DetermineStyle(textType);
			this.Builder.WriteHyperlink(
				text,
				textStyle,
				this.HyperlinkStyle,
				this.NavigateToCommand,
				target
				);
		}
		#endregion

		protected void AppendElement(Inline element)
		{
			if (element == null) throw new ArgumentNullException("element");

			this.EnsureIndent();
			this.Builder.AppendElement(element);
		}

		protected override void WriteLineBreakOverride()
		{
			this.Builder.WriteLineBreak();
		}
	}
}
